package com.slipper.filter;

import java.io.IOException;
import java.text.DecimalFormat;
import java.util.*;

import com.slipper.PaasContext;
import com.slipper.common.bo.UserDetail;
import com.slipper.constants.Constants;
import org.springframework.util.AntPathMatcher;
import com.slipper.exception.DecryptException;
import com.slipper.http.HttpRequestWrapper;
import com.slipper.system.log.bo.OperatorLogBO;
import com.slipper.system.log.controller.LogAsyncService;
import com.slipper.util.ResultUtil;
import com.slipper.util.Tool;
import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;

import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.slipper.core.gson.GsonUtil;

public class HttpRequestLoggerFilter implements Filter {
    private static final Logger logger = LoggerFactory.getLogger(HttpRequestLoggerFilter.class);

    private final List<String> allowedRepeatList = new ArrayList<>();
    private final AntPathMatcher matcher = new AntPathMatcher();

    public HttpRequestLoggerFilter(String allowedRepeatPath) {
        if (Tool.isBlank(allowedRepeatPath)) return;
        allowedRepeatList.addAll(Arrays.asList(allowedRepeatPath.split(",")));
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException {
        try {
            long startTime = System.currentTimeMillis();
            HttpServletRequest httpRequest = (HttpServletRequest) request;

            String body = "";
            String path = httpRequest.getServletPath();
            if (path.startsWith("/assets/")) {
                filterChain.doFilter(httpRequest, response);
                return;
            }

            if (!path.startsWith("/refresh")
                    && !path.startsWith("/v1")
                    && !path.startsWith("/actuator")
                    && !path.startsWith("/druid")) {
                boolean allowedRepeat = !allowedRepeatList.isEmpty()
                        && allowedRepeatList.stream().anyMatch(p -> matcher.match(p, path));
                HttpRequestWrapper requestWrapper = new HttpRequestWrapper(httpRequest, allowedRepeat);
                body = requestWrapper.getBody();
                httpRequest = requestWrapper;
            }

            showLogger(startTime, body, httpRequest);
            filterChain.doFilter(httpRequest, response);
        } catch (DecryptException e) {
            ResultUtil.failureResult((HttpServletResponse) response, 555, null);
        } catch (Exception e) {
            String url = ((HttpServletRequest) request).getServletPath();
            ResultUtil.failureResult((HttpServletResponse) response, "/setting".equals(url) ? 555 : 200, e.getMessage());
        }
    }

    private void showLogger(long startTime, String requestBody, HttpServletRequest request) {
        DecimalFormat df = new DecimalFormat("#0.##");
        UserDetail user = Constants.getSessionUser(request.getSession());
        String url = request.getServletPath();
        logger.info("operator: {}, time: {} s, url: {}, body: {}, params: {}, method: {}",
                user == null ? "游客" : (user.getEmployee().getName() + "(" +  user.getEmployeeId() + ")"),
                df.format((System.currentTimeMillis() - startTime) * 1.0 / 1000),
                url,
                requestBody,
                GsonUtil.toJson(request.getParameterMap()),
                request.getMethod());

        if (user == null
                || url.startsWith("/refresh")
                || url.startsWith("/actuator")
                || url.startsWith("/druid")) {
            return;
        }

        OperatorLogBO log = new OperatorLogBO();
        log.setOrgId(user.getOrgId());
        log.setOrgName(user.getOrgName());
        log.setUserId(user.getEmployeeId());
        log.setName(user.getEmployee().getName());
        log.setOpTime(System.currentTimeMillis());
        log.setUrl(Tool.split(url, 1000));
        log.setParams(Tool.split(requestBody, 2000));
        log.setTakeTime(System.currentTimeMillis() - startTime);
        log.setCreateTime(System.currentTimeMillis());
        PaasContext.getBean(LogAsyncService.class).saveOperatorLog(log);
    }
}
